library("easypackages")
library(tsibble)
library(tidyverse)
library(tidyquant)
library(plotly)
library(DT)
library(zoo)
library(lubridate)
library(fpp2)
library(fpp3)
library(scales)
library(patchwork)
library(feasts)
library(fable)
require(reshape)
packages("tidyverse", "tidyquant", "lubridate", "patchwork", "fpp2","fpp3","scales", "timetk")
Primer Serie de Tiempo
C <- read.csv('./RSCCASN.csv')
C
Teoría
Ventas minoristas anticipadas: Tiendas de ropa y accesorios de ropa
Las ventas minoristas Caracterizado por ser un negocio pequeño de origen familiar, se basa en la venta en menudeo o detallista es la empresa comercial o persona en régimen de autónomo que vende productos al consumidor final.
Saint Louis es una ciudad independiente del estado de Misuri, Estados Unidos. Está ubicada sobre la orilla derecha del río Misisipi.
Dataset
Unidades: Millones de dólares, sin ajuste estacional
Frecuencia: mensual
Con base a una Encuesta de ventas minoristas mensual anticipada (Censo)
Cita: Oficina del Censo de EE. UU., Ventas minoristas anticipadas: Tiendas de ropa y accesorios de ropa [RSCCASN], obtenido de FRED, Banco de la Reserva Federal de St. Louis; https://fred.stlouisfed.org/series/RSCCASN, 26 de noviembre de 2020.
Limpieza de datos Ropa
C$DATE <- as.Date(C$DATE,format="%Y-%m-%d")
C$RSCCASN <- as.double(C$RSCCASN)
C = rename(C, c(RSCCASN="Clothes", DATE='Date'))
C
Gráfica
p1 <- ggplot(data = C) +
geom_line(aes(x = Date, y = Clothes)) +
ylab("Millones de dólares") +
xlab("Fecha") + ggtitle("Ventas de ropa minoristas EE.UU. ")
p1

Aquí de puede identificar un OUTLIER empezando el año 2020 causado por la contingencia del COVID-19.
Segunda Serie de Tiempo
Food <- read.csv('./RSAFSNA.csv')
Food
Teoría
Ventas minoristas anticipadas: Servicios minoristas y de alimentos
Las ventas minoristas Caracterizado por ser un negocio pequeño de origen familiar, se basa en la venta en menudeo o detallista es la empresa comercial o persona en régimen de autónomo que vende productos al consumidor final.
Saint Louis es una ciudad independiente del estado de Misuri, Estados Unidos. Está ubicada sobre la orilla derecha del río Misisipi.
Los trabajadores del comercio de alimentos han surgido como una nueva categoría de servicios de primera línea durante esta pandemia.
Dataset
Fuente: Publicación de la Oficina del Censo de EE. UU .: Ventas mensuales anticipadas para servicios minoristas y de alimentos
Unidades: Millones de dólares, sin ajuste estacional
Frecuencia: mensual
Con base a una Encuesta de ventas minoristas mensual anticipada (Censo)
Cita: U.S. Census Bureau, Advance Retail Sales: Retail and Food Services, Total [RSAFSNA], obtenido de FRED, Federal Reserve Bank of St. Louis; https://fred.stlouisfed.org/series/RSAFSNA, 26 de noviembre de 2020.
Limpieza de datos Comida
Food$DATE <- as.Date(Food$DATE,format="%Y-%m-%d")
Food$RSAFSNA <- as.double(Food$RSAFSNA)
Food = rename(Food, c(RSAFSNA="Food", DATE='Date'))
Food
Gráfica
p2 <- ggplot(data = Food) +
geom_line(aes(x = Date, y = Food)) +
ylab("Millones de dólares") +
xlab("Fecha") +
ggtitle("Ventas de comida minoristas EE.UU. ")
p2

Graficar ambas
data <- merge (C, Food, by = "Date")
data
p3 <- ggplot(data = data) +
geom_line(aes(x = Date, y = Food)) +
geom_line(aes(x = Date, y = Clothes)) +
ylab("Millones de dólares") +
xlab("Fecha")
p3

data1 <- data %>%
select(Date,Clothes,Food)%>%
mutate(date = yearmonth(Date))%>%
as_tsibble(index = date)
data1
Descomposición STL
Aplicamos la descomposición llamada ST (Seasonal & Trend Decomposition)
Aquí se lleva a cabo la descomposición de la serie, como se puede ver en la tabla. La tendencia (trend) muestra el movimiento de la serie, sin considerar las fluctuaciones estacionales ni el residuo. Podemos analizar la tendencia de la serie gráficamente:
Serie de Ropa
dcmp1 <- data1 %>%
model(STL(Clothes))
components(dcmp1)
p1 <- data1 %>%
autoplot(Clothes, color='gray') +
autolayer(components(dcmp1), trend, color='red') +
xlab("Fecha") + ylab("Millones de dólares") +
ggtitle("Ventas de Ropa minoristas EE.UU. ")
dcmp2 <- data1 %>%
model(STL(Food))
components(dcmp2)
p2 <- data1 %>%
autoplot(Food, color='gray') +
autolayer(components(dcmp2), trend, color='red') +
xlab("Fecha") + ylab("Millones de dólares") +
ggtitle("Ventas de comida minoristas EE.UU. ")
p1/p2

Graficamos los tres componentes simultáneamente. (Serie “Ropa”)
components(dcmp1) %>% autoplot() + xlab("Year")

Graficamos todos los componentes de la serie “Comida”
components(dcmp2) %>% autoplot() + xlab("Year")

Podemos hacer entonces un pronóstico de nuestra serie de tiempo usando decomposition_model().
data1 %>%
model(stlf = decomposition_model(
STL(Clothes ~ trend(window = 7), robust = TRUE),
NAIVE(season_adjust)
)) %>%
forecast() %>%
autoplot(data1)+ ylab("Millones de dólares") +
ggtitle("Ropa ")

Serie de Comida.
data1 %>%
model(stlf = decomposition_model(
STL(Food ~ trend(window = 7), robust = TRUE),
NAIVE(season_adjust)
)) %>%
forecast() %>%
autoplot(data1)+ ylab("Millones de dólares") +
ggtitle("Comida")

Pronósticos con los modelos básicos
Ropa
train <- data1 %>% filter_index("2010-01-01" ~ "2016-01-01")
fit <- train %>%
model(
Mean = MEAN(Clothes),
`Naïve` = NAIVE(Clothes),
`Seasonal naïve` = SNAIVE(Clothes),
Drift = RW(Clothes ~ drift())
)
fc <- fit %>%
forecast(h = 80)
short <- data1 %>% filter_index("01-01-2010" ~ .)
fc %>%
autoplot(short, level = NULL) +
xlab("Año") + ylab("Millones de dólares") +
ggtitle("Pronóstico para la venta minorista de ropa") +
guides(colour=guide_legend(title="Forecast"))

fr <- data1 %>%
filter_index('2000 Jan'~'2019 Jan')
adjusted <- fr %>%
model(`Media`=MEAN(Clothes),
`Naive`=NAIVE(Clothes),
`Drift`=NAIVE(Clothes~drift()),
`Seasonal naïve` = SNAIVE(Clothes))
fc <- adjusted %>%
forecast(h=30)
fc %>%
autoplot(short)+ ylab("Millones de dólares") +
ggtitle("Ventas de ropa minorista")

Se puede ver que el modelo de predicción que mejor se ajusta a esta serie en particular es el Seasonal Naive, pues es una serie altamente estacional.
Diagnóstico de residuales
train %>%
model(SNAIVE(Clothes)) %>%
gg_tsresiduals() +
ggtitle("Diagnóstico de residuales para el modelo Seasonal Naïve")

Diagnóstico de residuales
En la primera gráfica de los residuos no se percibe ningún patrón fuertemente marcado ni tendencia, sino que los residuos parecen aleatorios y la media de los datos se acerca a cero.
La gráfica de ACF muestra pocos rezagos significativos.
El histograma muestra una distribución normal.
Tests de Portmanteau de autocorrelación
aug %>% features(.resid, box_pierce, lag=2, dof=0)
aug %>% features(.resid, ljung_box, lag=2, dof=0)
Se puede observar que en todos los modelos, de Box-Pierce y de Ljung-Box, se descarta la hipótesis nula (de que no es ruido blanco) ya que los p-value son muy pequeños e indica que existe una autocorrelación.
Comida
train <- data1 %>% filter_index("2010-01-01" ~ "2018-01-01")
fit <- train %>%
model(
Mean = MEAN(Food),
`Naïve` = NAIVE(Food),
`Seasonal naïve` = SNAIVE(Food),
Drift = RW(Food ~ drift())
)
fc <- fit %>%
forecast(h = 80)
fc %>%
autoplot(short, level = NULL) +
xlab("Año") + ylab("Millones de dólares") +
ggtitle("Pronóstico para la venta minorista de comida") +
guides(colour=guide_legend(title="Forecast"))

fr <- data1 %>%
filter_index('2000 Jan'~'2019 Jan')
adjusted <- fr %>%
model(`Media`=MEAN(Food),
`Naive`=NAIVE(Food),
`Drift`=NAIVE(Food~drift()),
`Seasonal naïve` = SNAIVE(Food))
fc <- adjusted %>%
forecast(h=30)
fc %>%
autoplot(short)+ylab("Millones de dólares") +
ggtitle("Ventas de comida minoristas ")

También en este caso el modelo que mas se ajusta a la serie es el Seasonal Naive
Diagnóstico de residuales
aug2 <- augment(fit)
aug2
train %>%
model(SNAIVE(Food)) %>%
gg_tsresiduals() +
ggtitle("Diagnóstico de residuales para el modelo Seasonal Naïve")

Diagnóstico de residuales
En la primera gráfica de los residuos no se percibe ningún patrón fuertemente marcado ni tendencia, sino que los residuos parecen aleatorios y la media de los datos se acerca a cero.
La gráfica de ACF muestra pocos rezagos significativos.
El histograma muestra una distribución que se acerca a una normal.
Tests de Portmanteau de autocorrelación
aug2 %>% features(.resid, box_pierce, lag=2, dof=0)
aug2 %>% features(.resid, ljung_box, lag=2, dof=0)
Se puede observar que en todos los modelos, de Box-Pierce y de Ljung-Box, se descarta la hipótesis nula (de que no es ruido blanco) ya que los p-value son muy pequeños e indica que existe una autocorrelación.
Suavización Exponencial
Ropa
# Entrenamiento de los modelos
fit3 <- train %>%
model(
`Seasonal naïve` = SNAIVE(Clothes),
`Damped Holt Winters` = ETS(Clothes ~ error("M") + trend("Ad") +
season("M")),
`ETS sin tendencia y aditivo` = ETS(Clothes ~ error("A") + trend("N") + season("A"))
)
# Pronóstico
fc <- fit3 %>%
forecast(h = 80)
a <- fc %>%
autoplot(short, level = NULL) +
xlab("Year") + ylab("Millones de dólares") +
ggtitle("Suavización exponencial vs Métodos de Referencia Ropa") +
guides(colour=guide_legend(title="Forecast"))
b <- a + tidyquant::coord_x_date(xlim = c("2017-01-01","2022-01-01")) + ggtitle("") +
theme(legend.position = "none")
(a) / (b)

Errores de pronóstico Ropa
The future dataset is incomplete, incomplete out-of-sample data will be treated as missing.
47 observations are missing between 2020 nov. and 2024 sep.
El mejor modelo es el de Seasonal Naïve, pues tiene los valores menores de error de predicción en todas las métricas.
Comida
# Entrenamiento de los modelos
fit3 <- train %>%
model(
`Seasonal naïve` = SNAIVE(Food),
`Damped Holt Winters` = ETS(Food ~ error("M") + trend("Ad") +
season("M")),
`ETS sin tendencia y aditivo` = ETS(Food ~ error("A") + trend("N") + season("A"))
)
# Pronóstico
fc <- fit3 %>%
forecast(h = 80)
a <- fc %>%
autoplot(short, level = NULL) +
xlab("Year") + ylab("Millones de dólares") +
ggtitle("Suavización exponencial vs Métodos de Referencia Food") +
guides(colour=guide_legend(title="Forecast"))
b <- a + tidyquant::coord_x_date(xlim = c("2017-01-01","2022-01-01")) + ggtitle("") +
theme(legend.position = "none")
(a) / (b)

Errores de pronóstico Comida
The future dataset is incomplete, incomplete out-of-sample data will be treated as missing.
47 observations are missing between 2020 nov. and 2024 sep.
Aquí el mejor modelo es el Holt-Winters Amortiguado, por tener los errores de pronóstico menores.
food_tsibble <- data1 %>%
select(date,Food)%>%
as_tsibble(index = date)
food_tsibble
Modelo Arima
Ropa
fit <- fr %>%
model(ARIMA(Clothes ~ PDQ(1,1,0),
stepwise = FALSE, approximation = FALSE))
report(fit)
Series: Clothes
Model: ARIMA(3,0,0)(1,1,0)[12] w/ drift
Coefficients:
ar1 ar2 ar3 sar1 constant
0.1947 0.2255 0.3919 -0.1167 97.1027
s.e. 0.0621 0.0616 0.0631 0.0714 37.7071
sigma^2 estimated as 340362: log likelihood=-1688.01
AIC=3388.02 AICc=3388.42 BIC=3408.3
fit <- fr %>%
model(ARIMA(Clothes ~ pdq(2,1,0) + PDQ(1,1,0)),
`Damped` = ETS(Clothes~error("A") + trend("Ad", phi = 0.9))
)
rev <- fit %>%
augment()
rev
Gráfica SARIMA
fit_arima_prueba <- fr %>%
model(ARIMA(Clothes ~ pdq(2,1,0) + PDQ(1,1,0)))
fc_arima_prueba <- fit_arima_prueba %>%
forecast(h=25)
fc_arima_prueba %>%
autoplot(short) +
ggtitle("Pronóstico Venta de ropa minorista 2019 y 2020") +
xlab("Año") + ylab ("Millones de dólares")

Gráfica Damped
fit <- fr %>%
model(`Damped` = ETS(Clothes~error("A") + trend("Ad", phi = 0.9)))
fc <- fit %>%
forecast(h=30)
fc %>%
autoplot(short)+
ggtitle("Pronóstico Venta de ropa minorista") +
xlab("Año") + ylab ("Millones de dólares")

Comida
fit <- fr %>%
model(ARIMA(Food ~ PDQ(1,1,0),
stepwise = FALSE, approximation = FALSE))
report(fit)
Series: Food
Model: ARIMA(4,0,1)(1,1,0)[12] w/ drift
Coefficients:
ar1 ar2 ar3 ar4 ma1 sar1 constant
1.2364 -0.0230 0.097 -0.3349 -0.8607 -0.1930 351.6589
s.e. 0.1865 0.1413 0.132 0.0689 0.2046 0.0805 72.6405
sigma^2 estimated as 59476383: log likelihood=-2247.75
AIC=4511.5 AICc=4512.19 BIC=4538.54
fit <- fr %>%
model(ARIMA(Food ~ pdq(2,1,0) + PDQ(1,1,0)),
`Damped` = ETS(Food~error("A") + trend("Ad", phi = 0.9))
)
rev <- fit %>%
augment()
rev
Gráfica SARIMA
fit_arima_prueba <- fr %>%
model(ARIMA(Food ~ pdq(2,1,0) + PDQ(1,1,0)))
fc_arima_prueba <- fit_arima_prueba %>%
forecast(h=25)
fc_arima_prueba %>%
autoplot(short) +
ggtitle("Pronóstico Venta de comida minorista 2019 y 2020") +
xlab("Año") + ylab ("Millones de dólares")

Gráfica Damped
fit <- fr %>%
model(`Damped` = ETS(Food~error("A") + trend("Ad", phi = 0.9)))
fc <- fit %>%
forecast(h=30)
fc %>%
autoplot(short)+
ggtitle("Pronóstico Venta de comida minorista") +
xlab("Año") + ylab ("Millones de dólares")

Se puede observar que el metodo Sarima se ajusta mejor a ambas series de tiempo.
Conclusión
De entre todos los modelos basicos de pronósticos para la serie de tiempo de las Ventas minoristas de tiendas de ropa y accesorios de ropa, el que mejor se ajustaba inicialmente es el Seasonal Naïve.
Para el metodo de suavizacion expponencial, el que mejor se ajustaba a los datos fue el modelo de Holt-Winters Amortiguado.
El método Damped y Arima terminan por ser mejores modelos para pronosticar, pero el método Arima Estacional es el mejor; tiene un mejor ajuste según los datos en ambas series de tiempo.
Por último podemos concluir que:
Para la serie de tiempo de la comida minorista: Modelo Holt Winters Amortiguado es muy bueno, pero el mejor es Arima estacional (Sarima) porque tiene los errores de pronóstico más pequeños.
Para la serie de tiempo de la venta de ropa: Damped fue muy buena, pero la mejor es Arima estacional (Sarima) porque tiene los errores de pronóstico más pequeños.
LS0tDQp0aXRsZTogIlByb3llY3RvIEZpbmFsOiBTZXJpZXMgZGUgVGllbXBvcyINCmF1dGhvcjogIkFuYSBNYXLDrWEgQWd1aWxlcmEiDQpkYXRlOiAyMDIwLTA0LTEyDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGdpdGh1Yl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGRldjoganBlZw0KYWx3YXlzX2FsbG93X2h0bWw6IHRydWUNCi0tLQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoImVhc3lwYWNrYWdlcyIpDQpsaWJyYXJ5KHRzaWJibGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeXF1YW50KQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZnBwMikNCmxpYnJhcnkoZnBwMykNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KGZlYXN0cykNCmxpYnJhcnkoZmFibGUpDQpyZXF1aXJlKHJlc2hhcGUpDQoNCnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCAidGlkeXF1YW50IiwgImx1YnJpZGF0ZSIsICJwYXRjaHdvcmsiLCAiZnBwMiIsImZwcDMiLCJzY2FsZXMiLCAidGltZXRrIikNCmBgYA0KDQoNCiMgUHJpbWVyIFNlcmllIGRlIFRpZW1wbw0KYGBge3J9DQpDIDwtIHJlYWQuY3N2KCcuL1JTQ0NBU04uY3N2JykNCkMNCg0KYGBgDQogDQojIyBUZW9yw61hDQoqKlZlbnRhcyBtaW5vcmlzdGFzIGFudGljaXBhZGFzOiBUaWVuZGFzIGRlIHJvcGEgeSBhY2Nlc29yaW9zIGRlIHJvcGEqKg0KDQpMYXMgdmVudGFzIG1pbm9yaXN0YXMNCkNhcmFjdGVyaXphZG8gcG9yIHNlciB1biBuZWdvY2lvIHBlcXVlw7FvIGRlIG9yaWdlbiBmYW1pbGlhciwgc2UgYmFzYSBlbiBsYSB2ZW50YSBlbiBtZW51ZGVvIG8gZGV0YWxsaXN0YSBlcyBsYSBlbXByZXNhIGNvbWVyY2lhbCBvIHBlcnNvbmEgZW4gcsOpZ2ltZW4gZGUgYXV0w7Nub21vIHF1ZSB2ZW5kZSBwcm9kdWN0b3MgYWwgY29uc3VtaWRvciBmaW5hbC4gDQoNClNhaW50IExvdWlzIGVzIHVuYSBjaXVkYWQgaW5kZXBlbmRpZW50ZSBkZWwgZXN0YWRvIGRlIE1pc3VyaSwgRXN0YWRvcyBVbmlkb3MuIEVzdMOhIHViaWNhZGEgc29icmUgbGEgb3JpbGxhIGRlcmVjaGEgZGVsIHLDrW8gTWlzaXNpcGkuDQoNCg0KKipEYXRhc2V0KioNCg0KVW5pZGFkZXM6IE1pbGxvbmVzIGRlIGTDs2xhcmVzLCBzaW4gYWp1c3RlIGVzdGFjaW9uYWwNCg0KRnJlY3VlbmNpYTogbWVuc3VhbA0KDQpDb24gYmFzZSBhIHVuYSBFbmN1ZXN0YSBkZSB2ZW50YXMgbWlub3Jpc3RhcyBtZW5zdWFsIGFudGljaXBhZGEgKENlbnNvKQ0KDQpDaXRhOg0KT2ZpY2luYSBkZWwgQ2Vuc28gZGUgRUUuIFVVLiwgVmVudGFzIG1pbm9yaXN0YXMgYW50aWNpcGFkYXM6IFRpZW5kYXMgZGUgcm9wYSB5IGFjY2Vzb3Jpb3MgZGUgcm9wYSBbUlNDQ0FTTl0sIG9idGVuaWRvIGRlIEZSRUQsIEJhbmNvIGRlIGxhIFJlc2VydmEgRmVkZXJhbCBkZSBTdC4gTG91aXM7IGh0dHBzOi8vZnJlZC5zdGxvdWlzZmVkLm9yZy9zZXJpZXMvUlNDQ0FTTiwgMjYgZGUgbm92aWVtYnJlIGRlIDIwMjAuDQoNCg0KDQojIyBMaW1waWV6YSBkZSBkYXRvcyBSb3BhDQpgYGB7cn0NCkMkREFURSA8LSBhcy5EYXRlKEMkREFURSxmb3JtYXQ9IiVZLSVtLSVkIikNCg0KQyRSU0NDQVNOIDwtIGFzLmRvdWJsZShDJFJTQ0NBU04pIA0KYGBgDQoNCmBgYHtyfQ0KQyA9IHJlbmFtZShDLCBjKFJTQ0NBU049IkNsb3RoZXMiLCBEQVRFPSdEYXRlJykpDQpDDQpgYGANCg0KIyMgR3LDoWZpY2ENCmBgYHtyfQ0KcDEgPC0gZ2dwbG90KGRhdGEgPSBDKSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gQ2xvdGhlcykpICsNCiAgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArIA0KICB4bGFiKCJGZWNoYSIpICsgZ2d0aXRsZSgiVmVudGFzIGRlIHJvcGEgbWlub3Jpc3RhcyBFRS5VVS4gIikNCnAxDQpgYGANCkFxdcOtIGRlIHB1ZWRlIGlkZW50aWZpY2FyIHVuIE9VVExJRVIgZW1wZXphbmRvIGVsIGHDsW8gMjAyMCBjYXVzYWRvIHBvciBsYSBjb250aW5nZW5jaWEgZGVsIENPVklELTE5Lg0KDQoNCiMgU2VndW5kYSBTZXJpZSBkZSBUaWVtcG8NCmBgYHtyfQ0KRm9vZCA8LSByZWFkLmNzdignLi9SU0FGU05BLmNzdicpDQpGb29kDQpgYGANCg0KIyMgVGVvcsOtYQ0KKipWZW50YXMgbWlub3Jpc3RhcyBhbnRpY2lwYWRhczogU2VydmljaW9zIG1pbm9yaXN0YXMgeSBkZSBhbGltZW50b3MqKg0KDQpMYXMgdmVudGFzIG1pbm9yaXN0YXMNCkNhcmFjdGVyaXphZG8gcG9yIHNlciB1biBuZWdvY2lvIHBlcXVlw7FvIGRlIG9yaWdlbiBmYW1pbGlhciwgc2UgYmFzYSBlbiBsYSB2ZW50YSBlbiBtZW51ZGVvIG8gZGV0YWxsaXN0YSBlcyBsYSBlbXByZXNhIGNvbWVyY2lhbCBvIHBlcnNvbmEgZW4gcsOpZ2ltZW4gZGUgYXV0w7Nub21vIHF1ZSB2ZW5kZSBwcm9kdWN0b3MgYWwgY29uc3VtaWRvciBmaW5hbC4gDQoNClNhaW50IExvdWlzIGVzIHVuYSBjaXVkYWQgaW5kZXBlbmRpZW50ZSBkZWwgZXN0YWRvIGRlIE1pc3VyaSwgRXN0YWRvcyBVbmlkb3MuIEVzdMOhIHViaWNhZGEgc29icmUgbGEgb3JpbGxhIGRlcmVjaGEgZGVsIHLDrW8gTWlzaXNpcGkuDQoNCkxvcyB0cmFiYWphZG9yZXMgZGVsIGNvbWVyY2lvIGRlIGFsaW1lbnRvcyBoYW4gc3VyZ2lkbyBjb21vIHVuYSBudWV2YSBjYXRlZ29yw61hIGRlIHNlcnZpY2lvcyBkZSBwcmltZXJhIGzDrW5lYSBkdXJhbnRlIGVzdGEgcGFuZGVtaWEuIA0KDQoqKkRhdGFzZXQqKg0KDQpGdWVudGU6IFB1YmxpY2FjacOzbiBkZSBsYSBPZmljaW5hIGRlbCBDZW5zbyBkZSBFRS4gVVUgLjogVmVudGFzIG1lbnN1YWxlcyBhbnRpY2lwYWRhcyBwYXJhIHNlcnZpY2lvcyBtaW5vcmlzdGFzIHkgZGUgYWxpbWVudG9zDQoNClVuaWRhZGVzOiBNaWxsb25lcyBkZSBkw7NsYXJlcywgc2luIGFqdXN0ZSBlc3RhY2lvbmFsDQoNCkZyZWN1ZW5jaWE6IG1lbnN1YWwNCg0KDQpDb24gYmFzZSBhIHVuYSBFbmN1ZXN0YSBkZSB2ZW50YXMgbWlub3Jpc3RhcyBtZW5zdWFsIGFudGljaXBhZGEgKENlbnNvKQ0KDQpDaXRhOg0KVS5TLiBDZW5zdXMgQnVyZWF1LCBBZHZhbmNlIFJldGFpbCBTYWxlczogUmV0YWlsIGFuZCBGb29kIFNlcnZpY2VzLCBUb3RhbCBbUlNBRlNOQV0sIG9idGVuaWRvIGRlIEZSRUQsIEZlZGVyYWwgUmVzZXJ2ZSBCYW5rIG9mIFN0LiBMb3VpczsgaHR0cHM6Ly9mcmVkLnN0bG91aXNmZWQub3JnL3Nlcmllcy9SU0FGU05BLCAyNiBkZSBub3ZpZW1icmUgZGUgMjAyMC4NCg0KDQojIyBMaW1waWV6YSBkZSBkYXRvcyBDb21pZGENCmBgYHtyfQ0KRm9vZCREQVRFIDwtIGFzLkRhdGUoRm9vZCREQVRFLGZvcm1hdD0iJVktJW0tJWQiKQ0KDQpGb29kJFJTQUZTTkEgPC0gYXMuZG91YmxlKEZvb2QkUlNBRlNOQSkgDQpgYGANCg0KYGBge3J9DQpGb29kID0gcmVuYW1lKEZvb2QsIGMoUlNBRlNOQT0iRm9vZCIsIERBVEU9J0RhdGUnKSkNCkZvb2QNCmBgYA0KDQoNCiMjIEdyw6FmaWNhDQpgYGB7cn0NCnAyIDwtIGdncGxvdChkYXRhID0gRm9vZCkgKyANCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IEZvb2QpKSArDQogIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKyANCiAgeGxhYigiRmVjaGEiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSBjb21pZGEgbWlub3Jpc3RhcyBFRS5VVS4gIikNCnAyDQpgYGANCiMjIEdyYWZpY2FyIGFtYmFzDQpgYGB7cn0NCmRhdGEgPC0gbWVyZ2UgKEMsIEZvb2QsIGJ5ID0gIkRhdGUiKQ0KZGF0YQ0KYGBgDQoNCg0KYGBge3J9DQpwMyA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEpICsgDQogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBGb29kKSkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gQ2xvdGhlcykpICsgICAgICAgICAgICANCiAgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArIA0KICB4bGFiKCJGZWNoYSIpDQpwMw0KYGBgDQoNCg0KYGBge3J9DQpkYXRhMSA8LSBkYXRhICU+JQ0KICBzZWxlY3QoRGF0ZSxDbG90aGVzLEZvb2QpJT4lDQogIG11dGF0ZShkYXRlID0geWVhcm1vbnRoKERhdGUpKSU+JQ0KICBhc190c2liYmxlKGluZGV4ID0gZGF0ZSkNCg0KZGF0YTENCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90bHk6OmdncGxvdGx5KHAzKSANCmBgYA0KDQoNCiMgRGVzY29tcG9zaWNpw7NuIFNUTA0KDQpBcGxpY2Ftb3MgbGEgZGVzY29tcG9zaWNpw7NuIGxsYW1hZGEgU1QgKFNlYXNvbmFsICYgVHJlbmQgRGVjb21wb3NpdGlvbikNCg0KQXF1w60gc2UgbGxldmEgYSBjYWJvIGxhIGRlc2NvbXBvc2ljacOzbiBkZSBsYSBzZXJpZSwgY29tbyBzZSBwdWVkZSB2ZXIgZW4gbGEgdGFibGEuIExhIHRlbmRlbmNpYSAodHJlbmQpIG11ZXN0cmEgZWwgbW92aW1pZW50byBkZSBsYSBzZXJpZSwgc2luIGNvbnNpZGVyYXIgbGFzIGZsdWN0dWFjaW9uZXMgZXN0YWNpb25hbGVzIG5pIGVsIHJlc2lkdW8uIFBvZGVtb3MgYW5hbGl6YXIgbGEgdGVuZGVuY2lhIGRlIGxhIHNlcmllIGdyw6FmaWNhbWVudGU6DQoNCiMjIFNlcmllIGRlIFJvcGENCmBgYHtyfQ0KDQpkY21wMSA8LSBkYXRhMSAlPiUNCiAgbW9kZWwoU1RMKENsb3RoZXMpKQ0KDQpjb21wb25lbnRzKGRjbXAxKQ0KDQoNCnAxIDwtIGRhdGExICU+JQ0KICBhdXRvcGxvdChDbG90aGVzLCBjb2xvcj0nZ3JheScpICsNCiAgYXV0b2xheWVyKGNvbXBvbmVudHMoZGNtcDEpLCB0cmVuZCwgY29sb3I9J3JlZCcpICsNCiAgeGxhYigiRmVjaGEiKSArIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKw0KICBnZ3RpdGxlKCJWZW50YXMgZGUgUm9wYSBtaW5vcmlzdGFzIEVFLlVVLiAiKQ0KDQpkY21wMiA8LSBkYXRhMSAlPiUNCiAgbW9kZWwoU1RMKEZvb2QpKQ0KDQpjb21wb25lbnRzKGRjbXAyKQ0KDQpwMiA8LSBkYXRhMSAlPiUNCiAgYXV0b3Bsb3QoRm9vZCwgY29sb3I9J2dyYXknKSArDQogIGF1dG9sYXllcihjb21wb25lbnRzKGRjbXAyKSwgdHJlbmQsIGNvbG9yPSdyZWQnKSArDQogIHhsYWIoIkZlY2hhIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiVmVudGFzIGRlIGNvbWlkYSBtaW5vcmlzdGFzIEVFLlVVLiAiKQ0KDQpwMS9wMg0KYGBgDQoNCkdyYWZpY2Ftb3MgbG9zIHRyZXMgY29tcG9uZW50ZXMgc2ltdWx0w6FuZWFtZW50ZS4gKFNlcmllICJSb3BhIikNCg0KYGBge3J9DQpjb21wb25lbnRzKGRjbXAxKSAlPiUgYXV0b3Bsb3QoKSArIHhsYWIoIlllYXIiKQ0KYGBgDQoNCkdyYWZpY2Ftb3MgdG9kb3MgbG9zIGNvbXBvbmVudGVzIGRlIGxhIHNlcmllICJDb21pZGEiDQoNCmBgYHtyfQ0KY29tcG9uZW50cyhkY21wMikgJT4lIGF1dG9wbG90KCkgKyB4bGFiKCJZZWFyIikNCmBgYA0KUG9kZW1vcyBoYWNlciBlbnRvbmNlcyB1biBwcm9uw7NzdGljbyBkZSBudWVzdHJhIHNlcmllIGRlIHRpZW1wbyB1c2FuZG8gZGVjb21wb3NpdGlvbl9tb2RlbCgpLg0KDQpgYGB7cn0NCmRhdGExICU+JQ0KICBtb2RlbChzdGxmID0gZGVjb21wb3NpdGlvbl9tb2RlbCgNCiAgICAgICAgICAgICBTVEwoQ2xvdGhlcyB+IHRyZW5kKHdpbmRvdyA9IDcpLCByb2J1c3QgPSBUUlVFKSwNCiAgICAgICAgICAgICBOQUlWRShzZWFzb25fYWRqdXN0KQ0KICApKSAlPiUNCiAgZm9yZWNhc3QoKSAlPiUNCiAgYXV0b3Bsb3QoZGF0YTEpKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiUm9wYSAiKQ0KYGBgDQoNCg0KIyMgU2VyaWUgZGUgQ29taWRhLg0KDQpgYGB7cn0NCmRhdGExICU+JQ0KICBtb2RlbChzdGxmID0gZGVjb21wb3NpdGlvbl9tb2RlbCgNCiAgICAgICAgICAgICBTVEwoRm9vZCB+IHRyZW5kKHdpbmRvdyA9IDcpLCByb2J1c3QgPSBUUlVFKSwNCiAgICAgICAgICAgICBOQUlWRShzZWFzb25fYWRqdXN0KQ0KICApKSAlPiUNCiAgZm9yZWNhc3QoKSAlPiUNCiAgYXV0b3Bsb3QoZGF0YTEpKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiQ29taWRhIikNCmBgYA0KDQojIFByb27Ds3N0aWNvcyBjb24gbG9zIG1vZGVsb3MgYsOhc2ljb3MNCg0KIyMgUm9wYQ0KYGBge3J9DQp0cmFpbiA8LSBkYXRhMSAlPiUgZmlsdGVyX2luZGV4KCIyMDEwLTAxLTAxIiB+ICIyMDE2LTAxLTAxIikNCg0KZml0IDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBNZWFuID0gTUVBTihDbG90aGVzKSwNCiAgICBgTmHDr3ZlYCA9IE5BSVZFKENsb3RoZXMpLA0KICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKENsb3RoZXMpLA0KICAgIERyaWZ0ID0gUlcoQ2xvdGhlcyB+IGRyaWZ0KCkpDQogICkNCg0KZmMgPC0gZml0ICU+JQ0KICBmb3JlY2FzdChoID0gODApDQoNCnNob3J0IDwtIGRhdGExICU+JSBmaWx0ZXJfaW5kZXgoIjAxLTAxLTIwMTAiIH4gLikNCg0KDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiUHJvbsOzc3RpY28gcGFyYSBsYSB2ZW50YSBtaW5vcmlzdGEgZGUgcm9wYSIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQpgYGANCg0KDQpgYGB7cn0NCmZyIDwtIGRhdGExICU+JQ0KICBmaWx0ZXJfaW5kZXgoJzIwMDAgSmFuJ34nMjAxOSBKYW4nKQ0KDQphZGp1c3RlZCA8LSBmciAlPiUNCiAgbW9kZWwoYE1lZGlhYD1NRUFOKENsb3RoZXMpLA0KICAgICAgICBgTmFpdmVgPU5BSVZFKENsb3RoZXMpLA0KICAgICAgICBgRHJpZnRgPU5BSVZFKENsb3RoZXN+ZHJpZnQoKSksDQogICAgICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKENsb3RoZXMpKQ0KDQpmYyA8LSBhZGp1c3RlZCAlPiUNCiAgZm9yZWNhc3QoaD0zMCkNCg0KZmMgJT4lDQogIGF1dG9wbG90KHNob3J0KSsgeWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSByb3BhIG1pbm9yaXN0YSIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KYWNjdXJhY3koZml0KQ0KYGBgDQpTZSBwdWVkZSB2ZXIgcXVlIGVsIG1vZGVsbyBkZSBwcmVkaWNjacOzbiBxdWUgbWVqb3Igc2UgYWp1c3RhIGEgZXN0YSBzZXJpZSBlbiBwYXJ0aWN1bGFyIGVzIGVsIFNlYXNvbmFsIE5haXZlLCBwdWVzIGVzIHVuYSBzZXJpZSBhbHRhbWVudGUgZXN0YWNpb25hbC4NCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KYGBge3J9DQphdWcgPC0gYXVnbWVudChmaXQpDQphdWcNCmBgYA0KDQoNCg0KYGBge3Igd2FybmluZyA9IEZ9DQp0cmFpbiAlPiUgDQogIG1vZGVsKFNOQUlWRShDbG90aGVzKSkgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpICsgDQogIGdndGl0bGUoIkRpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzIHBhcmEgZWwgbW9kZWxvIFNlYXNvbmFsIE5hw692ZSIpDQpgYGANCiMjIERpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzDQoNCiogRW4gbGEgcHJpbWVyYSBncsOhZmljYSBkZSBsb3MgcmVzaWR1b3Mgbm8gc2UgcGVyY2liZSBuaW5nw7puIHBhdHLDs24gZnVlcnRlbWVudGUgbWFyY2FkbyBuaSB0ZW5kZW5jaWEsIHNpbm8gcXVlIGxvcyByZXNpZHVvcyBwYXJlY2VuIGFsZWF0b3Jpb3MgeSBsYSBtZWRpYSBkZSBsb3MgZGF0b3Mgc2UgYWNlcmNhIGEgY2Vyby4NCg0KKiBMYSBncsOhZmljYSBkZSBBQ0YgbXVlc3RyYSBwb2NvcyByZXphZ29zIHNpZ25pZmljYXRpdm9zLg0KDQoqIEVsIGhpc3RvZ3JhbWEgbXVlc3RyYSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuDQoNCg0KIyMgVGVzdHMgZGUgUG9ydG1hbnRlYXUgZGUgYXV0b2NvcnJlbGFjacOzbg0KDQpgYGB7cn0NCmF1ZyAlPiUgZmVhdHVyZXMoLnJlc2lkLCBib3hfcGllcmNlLCBsYWc9MiwgZG9mPTApDQpgYGANCg0KYGBge3J9DQphdWcgJT4lIGZlYXR1cmVzKC5yZXNpZCwgbGp1bmdfYm94LCBsYWc9MiwgZG9mPTApDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVuIHRvZG9zIGxvcyBtb2RlbG9zLCBkZSBCb3gtUGllcmNlIHkgZGUgTGp1bmctQm94LCBzZSBkZXNjYXJ0YSBsYSBoaXDDs3Rlc2lzIG51bGEgKGRlIHF1ZSBubyBlcyBydWlkbyBibGFuY28pIHlhIHF1ZSBsb3MgcC12YWx1ZSBzb24gbXV5IHBlcXVlw7FvcyBlIGluZGljYSBxdWUgZXhpc3RlIHVuYSBhdXRvY29ycmVsYWNpw7NuLg0KDQoNCiMjIENvbWlkYQ0KYGBge3J9DQp0cmFpbiA8LSBkYXRhMSAlPiUgZmlsdGVyX2luZGV4KCIyMDEwLTAxLTAxIiB+ICIyMDE4LTAxLTAxIikNCg0KZml0IDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBNZWFuID0gTUVBTihGb29kKSwNCiAgICBgTmHDr3ZlYCA9IE5BSVZFKEZvb2QpLA0KICAgIGBTZWFzb25hbCBuYcOvdmVgID0gU05BSVZFKEZvb2QpLA0KICAgIERyaWZ0ID0gUlcoRm9vZCB+IGRyaWZ0KCkpDQogICkNCg0KZmMgPC0gZml0ICU+JQ0KICBmb3JlY2FzdChoID0gODApDQoNCmZjICU+JQ0KICBhdXRvcGxvdChzaG9ydCwgbGV2ZWwgPSBOVUxMKSArDQogIHhsYWIoIkHDsW8iKSArIHlsYWIoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikgKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBwYXJhIGxhIHZlbnRhIG1pbm9yaXN0YSBkZSBjb21pZGEiKSArDQogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJGb3JlY2FzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpmciA8LSBkYXRhMSAlPiUNCiAgZmlsdGVyX2luZGV4KCcyMDAwIEphbid+JzIwMTkgSmFuJykNCg0KYWRqdXN0ZWQgPC0gZnIgJT4lDQogIG1vZGVsKGBNZWRpYWA9TUVBTihGb29kKSwNCiAgICAgICAgYE5haXZlYD1OQUlWRShGb29kKSwNCiAgICAgICAgYERyaWZ0YD1OQUlWRShGb29kfmRyaWZ0KCkpLA0KICAgICAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShGb29kKSkNCg0KZmMgPC0gYWRqdXN0ZWQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQoNCmZjICU+JQ0KICBhdXRvcGxvdChzaG9ydCkreWxhYigiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKSArDQogIGdndGl0bGUoIlZlbnRhcyBkZSBjb21pZGEgbWlub3Jpc3RhcyAiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmFjY3VyYWN5KGZpdCkNCmBgYA0KVGFtYmnDqW4gZW4gZXN0ZSBjYXNvIGVsIG1vZGVsbyBxdWUgbWFzIHNlIGFqdXN0YSBhIGxhIHNlcmllIGVzIGVsIFNlYXNvbmFsIE5haXZlDQoNCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KYGBge3J9DQphdWcyIDwtIGF1Z21lbnQoZml0KQ0KYXVnMg0KYGBgDQoNCg0KYGBge3Igd2FybmluZyA9IEZ9DQp0cmFpbiAlPiUgDQogIG1vZGVsKFNOQUlWRShGb29kKSkgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpICsgDQogIGdndGl0bGUoIkRpYWduw7NzdGljbyBkZSByZXNpZHVhbGVzIHBhcmEgZWwgbW9kZWxvIFNlYXNvbmFsIE5hw692ZSIpDQpgYGANCg0KIyMgRGlhZ27Ds3N0aWNvIGRlIHJlc2lkdWFsZXMNCg0KKiBFbiBsYSBwcmltZXJhIGdyw6FmaWNhIGRlIGxvcyByZXNpZHVvcyBubyBzZSBwZXJjaWJlIG5pbmfDum4gcGF0csOzbiBmdWVydGVtZW50ZSBtYXJjYWRvIG5pIHRlbmRlbmNpYSwgc2lubyBxdWUgbG9zIHJlc2lkdW9zIHBhcmVjZW4gYWxlYXRvcmlvcyB5IGxhIG1lZGlhIGRlIGxvcyBkYXRvcyBzZSBhY2VyY2EgYSBjZXJvLg0KDQoqIExhIGdyw6FmaWNhIGRlIEFDRiBtdWVzdHJhIHBvY29zIHJlemFnb3Mgc2lnbmlmaWNhdGl2b3MuDQoNCiogRWwgaGlzdG9ncmFtYSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIHF1ZSBzZSBhY2VyY2EgYSB1bmEgbm9ybWFsLg0KDQoNCiMjIFRlc3RzIGRlIFBvcnRtYW50ZWF1IGRlIGF1dG9jb3JyZWxhY2nDs24NCg0KYGBge3J9DQphdWcyICU+JSBmZWF0dXJlcygucmVzaWQsIGJveF9waWVyY2UsIGxhZz0yLCBkb2Y9MCkNCmBgYA0KDQpgYGB7cn0NCmF1ZzIgJT4lIGZlYXR1cmVzKC5yZXNpZCwgbGp1bmdfYm94LCBsYWc9MiwgZG9mPTApDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVuIHRvZG9zIGxvcyBtb2RlbG9zLCBkZSBCb3gtUGllcmNlIHkgZGUgTGp1bmctQm94LCBzZSBkZXNjYXJ0YSBsYSBoaXDDs3Rlc2lzIG51bGEgKGRlIHF1ZSBubyBlcyBydWlkbyBibGFuY28pIHlhIHF1ZSBsb3MgcC12YWx1ZSBzb24gbXV5IHBlcXVlw7FvcyBlIGluZGljYSBxdWUgZXhpc3RlIHVuYSBhdXRvY29ycmVsYWNpw7NuLg0KDQoNCiMgU3Vhdml6YWNpw7NuIEV4cG9uZW5jaWFsDQoNCiMjIFJvcGENCmBgYHtyfQ0KIyBFbnRyZW5hbWllbnRvIGRlIGxvcyBtb2RlbG9zDQpmaXQzIDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShDbG90aGVzKSwNCiAgICBgRGFtcGVkIEhvbHQgV2ludGVyc2AgPSBFVFMoQ2xvdGhlcyB+IGVycm9yKCJNIikgKyB0cmVuZCgiQWQiKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uKCJNIikpLA0KICAgIGBFVFMgc2luIHRlbmRlbmNpYSB5IGFkaXRpdm9gID0gRVRTKENsb3RoZXMgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpKQ0KICApDQoNCiMgUHJvbsOzc3RpY28NCmZjIDwtIGZpdDMgJT4lDQogIGZvcmVjYXN0KGggPSA4MCkNCg0KYSA8LSBmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiU3Vhdml6YWNpw7NuIGV4cG9uZW5jaWFsIHZzIE3DqXRvZG9zIGRlIFJlZmVyZW5jaWEgUm9wYSIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQoNCmIgPC0gIGEgKyB0aWR5cXVhbnQ6OmNvb3JkX3hfZGF0ZSh4bGltID0gYygiMjAxNy0wMS0wMSIsIjIwMjItMDEtMDEiKSkgKyBnZ3RpdGxlKCIiKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCihhKSAvIChiKQ0KDQpgYGANCg0KIyMgRXJyb3JlcyBkZSBwcm9uw7NzdGljbyBSb3BhDQpgYGB7cn0NCmFjY3VyYWN5KGZjLCBkYXRhMSkNCg0KYGBgDQoNCkVsIG1lam9yIG1vZGVsbyBlcyBlbCBkZSBTZWFzb25hbCBOYcOvdmUsIHB1ZXMgdGllbmUgbG9zIHZhbG9yZXMgbWVub3JlcyBkZSBlcnJvciBkZSBwcmVkaWNjacOzbiBlbiB0b2RhcyBsYXMgbcOpdHJpY2FzLg0KDQojIyBDb21pZGENCmBgYHtyfQ0KIyBFbnRyZW5hbWllbnRvIGRlIGxvcyBtb2RlbG9zDQpmaXQzIDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBgU2Vhc29uYWwgbmHDr3ZlYCA9IFNOQUlWRShGb29kKSwNCiAgICBgRGFtcGVkIEhvbHQgV2ludGVyc2AgPSBFVFMoRm9vZCB+IGVycm9yKCJNIikgKyB0cmVuZCgiQWQiKSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uKCJNIikpLA0KICAgIGBFVFMgc2luIHRlbmRlbmNpYSB5IGFkaXRpdm9gID0gRVRTKEZvb2QgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpKQ0KICApDQoNCiMgUHJvbsOzc3RpY28NCmZjIDwtIGZpdDMgJT4lDQogIGZvcmVjYXN0KGggPSA4MCkNCg0KYSA8LSBmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQsIGxldmVsID0gTlVMTCkgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJNaWxsb25lcyBkZSBkw7NsYXJlcyIpICsNCiAgZ2d0aXRsZSgiU3Vhdml6YWNpw7NuIGV4cG9uZW5jaWFsIHZzIE3DqXRvZG9zIGRlIFJlZmVyZW5jaWEgRm9vZCIpICsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IkZvcmVjYXN0IikpDQoNCmIgPC0gIGEgKyB0aWR5cXVhbnQ6OmNvb3JkX3hfZGF0ZSh4bGltID0gYygiMjAxNy0wMS0wMSIsIjIwMjItMDEtMDEiKSkgKyBnZ3RpdGxlKCIiKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCihhKSAvIChiKQ0KDQpgYGANCg0KIyMgRXJyb3JlcyBkZSBwcm9uw7NzdGljbyBDb21pZGENCmBgYHtyfQ0KYWNjdXJhY3koZmMsIGRhdGExKQ0KDQpgYGANCkFxdcOtIGVsIG1lam9yIG1vZGVsbyBlcyBlbCBIb2x0LVdpbnRlcnMgQW1vcnRpZ3VhZG8sIHBvciB0ZW5lciBsb3MgZXJyb3JlcyBkZSBwcm9uw7NzdGljbyBtZW5vcmVzLg0KDQoNCmBgYHtyfQ0KZm9vZF90c2liYmxlIDwtIGRhdGExICU+JQ0KICBzZWxlY3QoZGF0ZSxGb29kKSU+JQ0KICBhc190c2liYmxlKGluZGV4ID0gZGF0ZSkNCg0KZm9vZF90c2liYmxlDQpgYGANCg0KIyBNb2RlbG8gQXJpbWENCg0KIyMgUm9wYQ0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKENsb3RoZXMgfiBQRFEoMSwxLDApLA0KICAgICAgICAgICAgICBzdGVwd2lzZSA9IEZBTFNFLCBhcHByb3hpbWF0aW9uID0gRkFMU0UpKQ0KcmVwb3J0KGZpdCkNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCiAgbW9kZWwoQVJJTUEoQ2xvdGhlcyB+IHBkcSgyLDEsMCkgKyBQRFEoMSwxLDApKSwNCiAgICAgICAgYERhbXBlZGAgPSBFVFMoQ2xvdGhlc35lcnJvcigiQSIpICsgdHJlbmQoIkFkIiwgcGhpID0gMC45KSkNCiAgICAgICAgKQ0KcmV2IDwtIGZpdCAlPiUNCiAgYXVnbWVudCgpDQoNCnJldg0KYGBgDQoNCg0KYGBge3J9DQphY2N1cmFjeShmaXQpDQpgYGANCg0KIyMgR3LDoWZpY2EgU0FSSU1BDQpgYGB7cn0NCmZpdF9hcmltYV9wcnVlYmEgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKENsb3RoZXMgfiBwZHEoMiwxLDApICsgUERRKDEsMSwwKSkpDQoNCmZjX2FyaW1hX3BydWViYSA8LSBmaXRfYXJpbWFfcHJ1ZWJhICU+JQ0KICBmb3JlY2FzdChoPTI1KQ0KICAgICAgICANCmZjX2FyaW1hX3BydWViYSAlPiUgDQogIGF1dG9wbG90KHNob3J0KSArDQogIGdndGl0bGUoIlByb27Ds3N0aWNvIFZlbnRhIGRlIHJvcGEgbWlub3Jpc3RhIDIwMTkgeSAyMDIwIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KIyMgR3LDoWZpY2EgRGFtcGVkDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCm1vZGVsKGBEYW1wZWRgID0gRVRTKENsb3RoZXN+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpKQ0KDQpmYyA8LSBmaXQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQpKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSByb3BhIG1pbm9yaXN0YSIpICsNCiAgeGxhYigiQcOxbyIpICsgeWxhYiAoIk1pbGxvbmVzIGRlIGTDs2xhcmVzIikNCg0KYGBgDQoNCiMjIENvbWlkYQ0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKEZvb2QgfiBQRFEoMSwxLDApLA0KICAgICAgICAgICAgICBzdGVwd2lzZSA9IEZBTFNFLCBhcHByb3hpbWF0aW9uID0gRkFMU0UpKQ0KcmVwb3J0KGZpdCkNCg0KYGBgDQoNCg0KYGBge3J9DQpmaXQgPC0gZnIgJT4lDQogIG1vZGVsKEFSSU1BKEZvb2QgfiBwZHEoMiwxLDApICsgUERRKDEsMSwwKSksDQogICAgICAgIGBEYW1wZWRgID0gRVRTKEZvb2R+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpDQogICAgICAgICkNCnJldiA8LSBmaXQgJT4lDQogIGF1Z21lbnQoKQ0KcmV2DQpgYGANCg0KDQpgYGB7cn0NCmFjY3VyYWN5KGZpdCkNCg0KYGBgDQoNCiMjIEdyw6FmaWNhIFNBUklNQQ0KYGBge3J9DQpmaXRfYXJpbWFfcHJ1ZWJhIDwtIGZyICU+JQ0KICBtb2RlbChBUklNQShGb29kIH4gcGRxKDIsMSwwKSArIFBEUSgxLDEsMCkpKQ0KDQpmY19hcmltYV9wcnVlYmEgPC0gZml0X2FyaW1hX3BydWViYSAlPiUNCiAgZm9yZWNhc3QoaD0yNSkNCiAgICAgICAgDQpmY19hcmltYV9wcnVlYmEgJT4lIA0KICBhdXRvcGxvdChzaG9ydCkgKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSBjb21pZGEgbWlub3Jpc3RhIDIwMTkgeSAyMDIwIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KIyMgR3LDoWZpY2EgRGFtcGVkDQpgYGB7cn0NCmZpdCA8LSBmciAlPiUNCm1vZGVsKGBEYW1wZWRgID0gRVRTKEZvb2R+ZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIsIHBoaSA9IDAuOSkpKQ0KDQpmYyA8LSBmaXQgJT4lDQogIGZvcmVjYXN0KGg9MzApDQpmYyAlPiUNCiAgYXV0b3Bsb3Qoc2hvcnQpKw0KICBnZ3RpdGxlKCJQcm9uw7NzdGljbyBWZW50YSBkZSBjb21pZGEgbWlub3Jpc3RhIikgKw0KICB4bGFiKCJBw7FvIikgKyB5bGFiICgiTWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpgYGANCg0KU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVsIG1ldG9kbyBTYXJpbWEgc2UgYWp1c3RhIG1lam9yIGEgYW1iYXMgc2VyaWVzIGRlIHRpZW1wby4NCg0KDQojIENvbmNsdXNpw7NuDQoNCiogRGUgZW50cmUgdG9kb3MgbG9zIG1vZGVsb3MgYmFzaWNvcyBkZSBwcm9uw7NzdGljb3MgcGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGFzIFZlbnRhcyBtaW5vcmlzdGFzIGRlIHRpZW5kYXMgZGUgcm9wYSB5IGFjY2Vzb3Jpb3MgZGUgcm9wYSwgZWwgcXVlIG1lam9yIHNlIGFqdXN0YWJhIGluaWNpYWxtZW50ZSBlcyBlbCBTZWFzb25hbCBOYcOvdmUuDQoNCiogUGFyYSBlbCBtZXRvZG8gZGUgc3Vhdml6YWNpb24gZXhwcG9uZW5jaWFsLCBlbCBxdWUgbWVqb3Igc2UgYWp1c3RhYmEgYSBsb3MgZGF0b3MgZnVlIGVsIG1vZGVsbyBkZSBIb2x0LVdpbnRlcnMgQW1vcnRpZ3VhZG8uDQoNCiogRWwgbcOpdG9kbyBEYW1wZWQgeSBBcmltYSB0ZXJtaW5hbiBwb3Igc2VyIG1lam9yZXMgbW9kZWxvcyBwYXJhIHByb25vc3RpY2FyLCBwZXJvIGVsIG3DqXRvZG8gQXJpbWEgRXN0YWNpb25hbCBlcyBlbCBtZWpvcjsgdGllbmUgdW4gbWVqb3IgYWp1c3RlIHNlZ8O6biBsb3MgZGF0b3MgZW4gYW1iYXMgc2VyaWVzIGRlIHRpZW1wby4NCg0KKiBQb3Igw7psdGltbyBwb2RlbW9zIGNvbmNsdWlyIHF1ZTogDQogIC0gUGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGEgY29taWRhIG1pbm9yaXN0YToNCiAgTW9kZWxvIEhvbHQgV2ludGVycyBBbW9ydGlndWFkbyBlcyBtdXkgYnVlbm8sIHBlcm8gZWwgbWVqb3IgZXMgQXJpbWEgZXN0YWNpb25hbCAoU2FyaW1hKSBwb3JxdWUgdGllbmUgICAgIGxvcyBlcnJvcmVzIGRlIHByb27Ds3N0aWNvIG3DoXMgcGVxdWXDsW9zLg0KDQogIC0gUGFyYSBsYSBzZXJpZSBkZSB0aWVtcG8gZGUgbGEgdmVudGEgZGUgcm9wYToNCiAgRGFtcGVkIGZ1ZSBtdXkgYnVlbmEsIHBlcm8gbGEgbWVqb3IgZXMgQXJpbWEgZXN0YWNpb25hbCAoU2FyaW1hKSBwb3JxdWUgdGllbmUgbG9zIGVycm9yZXMgZGUgcHJvbsOzc3RpY28gbcOhcyBwZXF1ZcOxb3MuDQogIA==